/* Main window class */

#include <cassert>
#include <cstddef>

#include <sigc++/sigc++.h>

#include <glibmm/fileutils.h>
#include <glibmm/i18n.h>
#include <glibmm/main.h>
#include <glibmm/thread.h>
#include <glibmm/ustring.h>

#include <gdkmm/color.h>
#include <gdkmm/window.h>

#include <gtkmm/accelkey.h>
#include <gtkmm/actiongroup.h>
#include <gtkmm/clipboard.h>
#include <gtkmm/enums.h>
#include <gtkmm/filechooserdialog.h>
#include <gtkmm/filefilter.h>
#include <gtkmm/messagedialog.h>
#include <gtkmm/stock.h>
#include <gtkmm/toolbar.h>
#include <gtkmm/toolbutton.h>
#include <gtkmm/toolitem.h>
#include <gtkmm/uimanager.h>

#include "gtk_data_sink.hpp"

#include "main_window.hpp"

namespace memchart
{
	MainWindow::MainWindow (gtk_data_sink & main_thread)
		: main_thread(main_thread),
		ui_manager(Gtk::UIManager::create()),
		menu(Gtk::ActionGroup::create()),
		chart_align(Gtk::ALIGN_CENTER, Gtk::ALIGN_TOP, 0, 0),
		layout_list(chart.layout_list_model)
	{
		/* Create window layout */

		set_title("Memchart");
		set_default_size(800, 600);

		continue_action = Gtk::Action::create("Continue",
			Gtk::Stock::GO_FORWARD, "",
			_("Continue program execution (Space)"));
		continue_action->set_is_important();
		continue_action->set_sensitive(false);
		menu->add(continue_action,
			Gtk::AccelKey("space"),
			sigc::mem_fun(*this, &MainWindow::on_menu_continue));
		fullscreen_action = Gtk::Action::create("Fullscreen",
			Gtk::Stock::FULLSCREEN, "", _("Toggle fullscreen mode (Ctrl+F)"));
		menu->add(fullscreen_action,
			Gtk::AccelKey("<control>F"),
			sigc::mem_fun(*this, &MainWindow::on_menu_fullscreen));
		menu->add(
			Gtk::Action::create("Copy", Gtk::Stock::COPY, "",
				_("Copy current picture to the clipboard (Ctrl+C)")),
			Gtk::AccelKey("<control>C"),
			sigc::mem_fun(*this, &MainWindow::on_menu_copy));
		menu->add(
			Gtk::Action::create("SaveAs", Gtk::Stock::SAVE_AS, "",
				_("Save current picture to a file (Ctrl+S)")),
			Gtk::AccelKey("<control>S"),
			sigc::mem_fun(*this, &MainWindow::on_menu_save));
		Glib::RefPtr<Gtk::Action> action(Gtk::Action::create("Close",
			Gtk::Stock::CLOSE, "",
			_("Close this window and continue program execution (Ctrl+Q)")));
		action->set_is_important();
		menu->add(action,
			Gtk::AccelKey("<control>Q"),
			sigc::mem_fun(*this, &MainWindow::on_close));

		ui_manager->insert_action_group(menu);
		add_accel_group(ui_manager->get_accel_group());

		/*
		 * Looks like that having a menubar here is the easiest way to get
		 * accelerators working
		 */
		Glib::ustring menu_info =
			"<ui>"
				"<menubar name='Menubar'>"
					"<menuitem action='Continue' />"
					"<menuitem action='Fullscreen' />"
					"<menuitem action='Copy' />"
					"<menuitem action='SaveAs' />"
					"<menuitem action='Close' />"					
				"</menubar>"
				"<toolbar name='Toolbar'>"
					"<toolitem action='Continue' />"
					"<separator />"
					"<toolitem action='Fullscreen' />"
					"<separator />"
					"<toolitem action='Copy' />"
					"<toolitem action='SaveAs' />"
					"<separator />"
					"<separator expand='true' />"
					"<toolitem action='Close' />"
				"</toolbar>"
			"</ui>"
			;
		ui_manager->add_ui_from_string(menu_info);

		Gtk::Toolbar *toolbar = dynamic_cast<Gtk::Toolbar *>(
			ui_manager->get_widget("/Toolbar"));
		assert(toolbar != NULL);

		Gtk::ToolItem *layout_list_item = new Gtk::ToolItem;
		toolbar->insert(*Gtk::manage(layout_list_item), 7);
		layout_list_item->add(layout_list);
		layout_list.pack_start(chart.layout_list_columns.name);
		layout_list.set_tooltip_text(_("Choose preferred layout"));
		layout_list.signal_changed().connect(
			sigc::mem_fun(*this, &MainWindow::call_update));

		chart.signal_status_message.connect(
			sigc::mem_fun(*this, &MainWindow::status_message));
		chart.set_sensitive(false);
		chart_align.add(chart);
		chart_align.set_border_width(10);
		chart_evbox.add(chart_align);
		chart_evbox.modify_bg(Gtk::STATE_NORMAL, Gdk::Color("white"));
		chart_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
		chart_window.add(chart_evbox);
		chart_window.set_can_focus(false);

		vbox.pack_start(*toolbar, Gtk::PACK_SHRINK);
		vbox.pack_start(chart_window);
		vbox.pack_start(status_bar, Gtk::PACK_SHRINK);

		add(vbox);


		/* Initialize inter-thread connection */

		{
			Glib::Mutex::Lock lock(main_thread.mutex);

			main_thread.signal_update = &signal_update;
			main_thread.signal_quit = &signal_quit;
		}

		Glib::signal_idle().connect_once(
			sigc::mem_fun(*this, &MainWindow::on_start));

		signal_quit.connect(sigc::mem_fun(*this, &MainWindow::on_main_quit));
		signal_update.connect(sigc::mem_fun(*this, &MainWindow::on_update));


		/* All done */
		show_all_children();
	}


	/* Print a short message on status bar */
	void MainWindow::status_message (const Glib::ustring & s)
	{
		if (status_timeout)
		{
			status_bar.pop();
			status_timeout.disconnect();
		}
		status_bar.push(s);
		status_timeout = Glib::signal_timeout().connect_seconds(
			sigc::mem_fun(*this, &MainWindow::on_status_timeout), 3);
	}

	bool MainWindow::on_status_timeout ()
	{
		status_bar.pop();
		return false;
	}

	/* Executed once after the main loop has started */
	void MainWindow::on_start ()
	{
		Glib::Mutex::Lock lock(main_thread.mutex);

		/* Tell the main thread that signals are connected and running */
		main_thread.can_continue = true;
		main_thread.cond.signal();
	}

	void MainWindow::call_update ()
	{
		Gtk::TreeModel::iterator it(layout_list.get_active());
		chart.update(main_thread.objects, it ?
			(*it)[chart.layout_list_columns.id] : MemoryChart::LAYOUT_NONE);	
	}

	void MainWindow::on_update ()
	{
		continue_action->set_sensitive();
		chart.set_sensitive();

		Glib::Mutex::Lock lock(main_thread.mutex);

		status_bar.push(main_thread.update_msg);
		call_update();
		
		/* TODO: set focus on the Continue button */
	}

	void MainWindow::on_menu_continue ()
	{
		continue_action->set_sensitive(false);
		chart.set_sensitive(false);
		
		if (status_timeout)
		{
			status_bar.pop();
			status_timeout.disconnect();
		}
		status_bar.pop();

		Glib::Mutex::Lock lock(main_thread.mutex);

		main_thread.can_continue = true;
		main_thread.cond.signal();
	}

	void MainWindow::on_menu_fullscreen ()
	{
		if (get_window()->get_state() & Gdk::WINDOW_STATE_FULLSCREEN)
		{
			unfullscreen();
			fullscreen_action->set_stock_id(Gtk::Stock::FULLSCREEN);
		}
		else
		{
			fullscreen();
			fullscreen_action->set_stock_id(Gtk::Stock::LEAVE_FULLSCREEN);
		}
	}

	void MainWindow::on_menu_copy ()
	{
		Glib::RefPtr<Gtk::Clipboard> clip(Gtk::Clipboard::get());
		clip->set_image(chart.get_pixbuf());
		status_message(_("Picture copied to clipboard"));
	}

	void MainWindow::on_menu_save ()
	{
		Gtk::FileChooserDialog dialog(*this, _("Save snapshot"),
			Gtk::FILE_CHOOSER_ACTION_SAVE);

		dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
		dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK);
		dialog.set_do_overwrite_confirmation();
		
		Gtk::FileFilter filter_png;
		filter_png.set_name(_("PNG images"));
		filter_png.add_mime_type("image/png");
		dialog.add_filter(filter_png);
		
		if (dialog.run() == Gtk::RESPONSE_OK)
		{
			try
			{
				chart.get_pixbuf()->save(dialog.get_filename(), "png");
				status_message(_("Picture saved"));
			}
			catch (const Glib::FileError & e)
			{
				Gtk::MessageDialog msg(*this, _("Error saving snapshot"), false,
					Gtk::MESSAGE_ERROR);
				msg.set_secondary_text(e.what());
				msg.run();
			}
		}
	}

	void MainWindow::on_main_quit ()
	{
		/* Notify the user */
		Gtk::MessageDialog msg(*this, _("Main program execution has ended"));
		msg.run();

		hide();
	}

	void MainWindow::on_close ()
	{
		hide();
	}

	void MainWindow::on_hide ()
	{
		{
			Glib::Mutex::Lock lock(main_thread.mutex);

			main_thread.gui_active = false;

			/* In case the main thread signalled an update */
			main_thread.can_continue = true;
			main_thread.cond.signal();
		}

		Gtk::Widget::on_hide();
	}
}
